package com.dobbinsoft.iotechn.device.service.open;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dobbinsoft.iotechn.core.constant.Const;
import com.dobbinsoft.iotechn.core.exception.ServiceException;
import com.dobbinsoft.iotechn.core.model.Page;
import com.dobbinsoft.iotechn.device.api.open.DeviceService;
import com.dobbinsoft.iotechn.device.domain.DeviceDO;
import com.dobbinsoft.iotechn.device.domain.DeviceUserDO;
import com.dobbinsoft.iotechn.device.dto.UserDeviceDTO;
import com.dobbinsoft.iotechn.device.dto.DeviceUserDTO;
import com.dobbinsoft.iotechn.device.enums.DeviceUserStatusType;
import com.dobbinsoft.iotechn.device.enums.IotMessageType;
import com.dobbinsoft.iotechn.device.exception.DeviceExceptionDefinition;
import com.dobbinsoft.iotechn.device.exception.DeviceServiceException;
import com.dobbinsoft.iotechn.device.mapper.DeviceMapper;
import com.dobbinsoft.iotechn.device.mapper.DeviceUserMapper;
import com.dobbinsoft.iotechn.device.mqtt.IotMqttClient;
import com.dobbinsoft.iotechn.device.mqtt.message.down.IotDeviceCommandMessage;
import com.dobbinsoft.iotechn.user.api.biz.UserBizService;
import com.dobbinsoft.iotechn.user.domain.UserDO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Description:
 * User: rize
 * Date: 2020/6/23
 * Time: 14:43
 */
@Service("deviceService")
public class DeviceServiceImpl implements DeviceService {

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private DeviceUserMapper deviceUserMapper;

    @Autowired
    private IotMqttClient iotMqttClient;

    @Autowired
    private UserBizService userBizService;

    @Override
    @DS(Const.DB_M)
    @Transactional(rollbackFor = Exception.class)
    public String bindDevice(String accessKeyId, Long userId) throws ServiceException {
        DeviceDO deviceDO = deviceMapper.getDeviceUserIdForUpdate(accessKeyId);
        if (deviceDO == null) {
            throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_DEVICE_NOT_EXIST);
        }
        Date now = new Date();
        // 1. 尝试将用户设为设备管理员
        if (deviceDO.getUserId() == null) {
            // 将用户ID更新上去
            DeviceDO updateDeviceDO = new DeviceDO();
            updateDeviceDO.setId(deviceDO.getId());
            updateDeviceDO.setUserId(userId);
            updateDeviceDO.setGmtUpdate(now);
            deviceMapper.updateById(updateDeviceDO);
            // 冗余一份设备用户，方便查询
            DeviceUserDO newDeviceUser = this.buildNewDeviceUser(deviceDO.getId(), userId, DeviceUserStatusType.AGREED.getCode(), now);
            deviceUserMapper.insert(newDeviceUser);
            return "ok";
        } else if (deviceDO.getUserId().longValue() == userId.longValue()) {
            // 重复绑定
            throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_YOU_ARE_THE_USER_OF_THIS_DEVICE);
        }
        // 2. 尝试将用户设置为普通用户
        Integer count = deviceUserMapper.selectCount(new QueryWrapper<DeviceUserDO>().eq("device_id", deviceDO.getId()).eq("user_id", userId));
        if (count != 0) {
            throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_YOU_ARE_THE_USER_OF_THIS_DEVICE);
        }
        // 2.2. 绑定用户，并将其设为审核状态
        DeviceUserDO newDeviceUser = this.buildNewDeviceUser(deviceDO.getId(), userId, DeviceUserStatusType.WAIT_AUDIT.getCode(), now);
        deviceUserMapper.insert(newDeviceUser);
        return "wait";
    }

    private DeviceUserDO buildNewDeviceUser(Long deviceId, Long userId, Integer status, Date now) {
        DeviceUserDO newDeviceUser = new DeviceUserDO();
        newDeviceUser.setDeviceId(deviceId);
        newDeviceUser.setUserId(userId);
        newDeviceUser.setStatus(status);
        newDeviceUser.setGmtCreate(now);
        newDeviceUser.setGmtUpdate(now);
        return newDeviceUser;
    }

    @Override
    public Page<UserDeviceDTO> listDevices(Integer pageNo, Integer pageSize, Long userId) throws ServiceException {
        // TODO 缓存
        return deviceUserMapper.getDevicePage(Page.div(pageNo, pageSize, UserDeviceDTO.class), userId);
    }

    @Override
    public String sendCmd(Long deviceId, String cmd, JSONObject param, Long userId) throws ServiceException {
        // TODO 缓存
        // 校验设备所属权
        Integer count = deviceUserMapper.selectCount(new QueryWrapper<DeviceUserDO>().eq("device_id", deviceId).eq("user_id", userId));
        if (count == 0) {
            throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_NOT_BELONGS_TO_YOU);
        }
        // 向队列发送消息
        DeviceDO deviceDO = deviceMapper.selectById(deviceId);
        String accessKeyId = deviceDO.getAccessKeyId();
        IotDeviceCommandMessage iotDeviceCommand = new IotDeviceCommandMessage();
        iotDeviceCommand.setCode(IotMessageType.TO_DEVICE.getCode());
        iotDeviceCommand.setCmd(cmd);
        iotDeviceCommand.setParam(param);
        if (iotMqttClient.send(accessKeyId, iotDeviceCommand)) {
            return "ok";
        }
        throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_SEND_COMMAND_FAILED);
    }

    @Override
    @DS(Const.DB_S)
    public List<DeviceUserDTO> listDeviceUser(Long deviceId, Long userId) throws ServiceException {
        // 1. 判断用户是否是该设备成员
        Integer count = deviceUserMapper.selectCount(
                new QueryWrapper<DeviceUserDO>()
                        .eq("device_id", deviceId)
                        .eq("user_id", userId)
                        .eq("status", DeviceUserStatusType.AGREED.getCode()));
        if (count > 0) {
            List<DeviceUserDO> deviceUserList = deviceUserMapper.selectList(
                    new QueryWrapper<DeviceUserDO>()
                            .eq("device_id", deviceId)
                            .eq("status", DeviceUserStatusType.AGREED.getCode()));
            // 2. 去查用户头像与昵称
            List<Long> userIds = deviceUserList.stream().map(item -> item.getUserId()).collect(Collectors.toList());
            Map<Long, UserDO> usersNicknameAndAvatarMap = userBizService.getUsersNicknameAndAvatar(userIds).stream().collect(Collectors.toMap(UserDO::getId, item -> item));
            List<DeviceUserDTO> list = deviceUserList.stream().map((item) -> {
                UserDO userDO = usersNicknameAndAvatarMap.get(item.getUserId());
                DeviceUserDTO deviceUserDTO = new DeviceUserDTO();
                BeanUtils.copyProperties(item, deviceUserDTO);
                deviceUserDTO.setNickname(userDO.getNickname());
                deviceUserDTO.setAvatarUrl(userDO.getAvatarUrl());
                return deviceUserDTO;
            }).collect(Collectors.toList());
            return list;
        }
        throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_NOT_BELONGS_TO_YOU);
    }

    @Override
    @DS(Const.DB_S)
    public List<DeviceUserDTO> listDeviceUserWaitAudit(Long userId) throws ServiceException {
        // 列举出所有需要该用户审核的列表
        List<DeviceUserDTO> deviceUserWaitAudit = deviceUserMapper.getDeviceUserWaitAudit(userId);
        if (CollectionUtils.isEmpty(deviceUserWaitAudit)) {
            // 若为空，则不需要再封装用户头像了
            return deviceUserWaitAudit;
        }
        List<Long> userIds = deviceUserWaitAudit.stream().map(DeviceUserDTO::getUserId).collect(Collectors.toList());
        Map<Long, UserDO> usersNicknameAndAvatarMap = userBizService.getUsersNicknameAndAvatar(userIds).stream().collect(Collectors.toMap(UserDO::getId, item -> item));
        deviceUserWaitAudit.stream().forEach(item -> {
            UserDO userDO = usersNicknameAndAvatarMap.get(item.getUserId());
            item.setNickname(userDO.getNickname());
            item.setAvatarUrl(userDO.getAvatarUrl());
        });
        return deviceUserWaitAudit;
    }

    @Override
    @DS(Const.DB_M)
    @Transactional(rollbackFor = Exception.class)
    public String auditDeviceUser(Long deviceUserId, Integer status, Long userId) throws ServiceException {
        // 1.锁定审核行
        DeviceUserDO deviceUserDO = deviceUserMapper.getDeviceUserForUpdate(deviceUserId);
        // 2.校验设备管理员是否是当前用户
        Integer count = deviceMapper.selectCount
                (new QueryWrapper<DeviceDO>()
                        .eq("id", deviceUserDO.getDeviceId())
                        .eq("user_id", userId));
        if (count > 0) {
            // 3.将最新的状态更新到设备用户中
            DeviceUserDO updateDeviceUserDO = new DeviceUserDO();
            updateDeviceUserDO.setId(deviceUserId);
            updateDeviceUserDO.setStatus(status);
            updateDeviceUserDO.setGmtUpdate(new Date());
            deviceUserMapper.updateById(updateDeviceUserDO);
            return "ok";
        }
        throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_NOT_BELONGS_TO_YOU);
    }
}
